/*
* ============================================================================
*  Name        : sea_manager.h
*  Part of     : Finite state machine framework
*  Description : 
*  Version     : %version: %
*  
*
* ============================================================================
*/
/**
 * @file sea_manager.h
 * @brief
 * @ref sea::Manager
 *
 * @ingroup StateMachineFramework
 */
#if !defined( __sea_sm_manager_h__ )
#define __sea_sm_manager_h__

#include "sea.h"

namespace sea
{
    class Object;

    /** 
     * @class Manager sea_manager.h "sea_manager.h"
     * @brief 
     *   State Machine Manager/Dispatcher is created per thread basis. Keeps track of  <br>
     *  all state machines in the thread, handles incoming events and state  <br>
     *  machine's duty cycle. <br>
     *
     * @ingroup StateMachineFramework
     */
    class Manager : public OsThread
    {
        friend class Engine;
        friend class StateMachine;
        friend class SystemStateMachine;

    public:
        /**
         * Manager's dispatcher. The API allows integration of the 
         * state machine framework into existing event handling systems, like Qt
         *
         * @param process_all_events If true, all events queued for processing will be handled
         *
         * @return @li eOk                      If state machine is still active
         *         @li eStateMachineNotActive   State machine finished or never been launched
         */
        Error dispatch( bool process_all_events = true );

        /**
         * Join state machine to the manager/dispather
         *
         * @param state_machine State machine that wants to join the manager
         * @param parent Parent
         *
         * @return @li eOk              On success
         *         @li eWrongParameter  state_machine is NULL
         */
        Error join( StateMachine* state_machine, StateMachine* parent = NULL );

        /**
         * Set event callback, which gets invoked on any event sent to the dispather.
         * Use this API along with dispatch( ) API, so state machine manager can 
         * signal hosting system to call dispatch( ) to process machine's
         * event(s)
         *
         * @param callback Event callback
         * @param param Callback Parameter
         */
        void set_event_callback( Callback callback, void* param )
        { 
            OsMutex::Scoped protect( &m_lock );
            m_dispather_host_callback = callback; 
            m_dispather_host_param = param;
        }

    private:

        Manager( );
        ~Manager( );

        /**
         * Manager's thread function. All state machines under the manager control run in the 
         * context of this thread. \b INTERNAL \b API. 
         */
        void exec( );

        /**
         * Top level event processing. Event picked up from manager's system/internal/external queue is passed to this 
         * function to execute an action associated with the event. \b INTERNAL \b API. 
         *
         * @param event Incoming event
         *
         * @return bool     @li true  state machine is still alive
         *                  @li false state machine has been destroyed
         */
        bool handle_event( Event* event );

        /**
         * Search the state and invoke action function(s). \b INTERNAL \b API. 
         *
         * @param state_machine State machine
         * @param event Event
         * @param state_index State index. Either current state or exceptions
         * @param next Reference to next state index
         */
        bool invoke_action( StateMachine* state_machine, Event* event, i::Offset state_offset, i::Offset& next );

        /**
         * When dispatch API is utilized the callback will be invoked
         * to signal the host system to call Manager::dispatch( )
         */
        void dispatch_host( )
        { 
            if ( m_dispather_host_callback )
            {
                ( *m_dispather_host_callback )( m_dispather_host_param );
            }
        }

        /**
         * Get critical section. \b INTERNAL \b API. 
         *
         * @return OsMutex&
         */
        OsMutex* lock( ) { return &m_lock; }

        /**
         * Retrieve system event queue. \b INTERNAL \b API. 
         * 
         * @return Queue&  system event queue
         */
        Queue* system_event_queue( ) { return &m_system_events; }

        /**
         * Retrieve internal event queue. \b INTERNAL \b API. 
         * 
         * @return Queue&  internal event queue
         */
        Queue* internal_event_queue( ) { return &m_interal_events; }

        /**
         * Retrieve external event queue. \b INTERNAL \b API. 
         * 
         * @return Queue&  external event queue
         */
        Queue* external_event_queue( ) { return &m_external_events; }

        /**
         * Trigger event dispatcher. \b INTERNAL \b API. 
         */
        void add_event( )
        { 
            if ( !m_is_parser )
            {
                m_event_queue_signal.signal( ); 
            }
        }

        /**
         * Retrieve OS thread handle. \b INTERNAL \b API. 
         *
         * @return OsThreadHandle
         */
        OsThreadHandle get_thread_handle( ) { return m_thread_handle; }

        /**
         * Reset thread handle for the manager to be reused. \b INTERNAL \b API. 
         */
        void reset_thread_handle( ) { m_thread_handle = 0; }

        /**
         * Wait for the manager finish. This call should be made from the thread. \b INTERNAL \b API. 
         * other than the manager's
         *
         * @return Error eOk if manager finished successfully, eFailed 
         *  on attempt to wait state machine in the same thread
         */
        Error wait( );

        /** 
         * Broadcast exit event to all state machines under manager's control. \b INTERNAL \b API. 
         */
        void destroy( );

        /** 
         * Wipe out event queues. \b INTERNAL \b API. 
         */
        void clean_event_queues( );

        /** 
         * Avoid waiting for the semaphore for parsing tasks. \b INTERNAL \b API. 
         *
         * @param aTrue 
         */
        void set_parser_mode( bool aTrue ){ m_is_parser = aTrue; }

        /**
         * Log functions. \b INTERNAL \b API. 
         */
#if defined( SEA_LOG )

        void log_state_transition( StateMachine* state_machine, i::Offset state );
        void log_event( StateMachine* state_machine, i::Offset event_record_offset );
        void log_unrecognized_event( StateMachine* state_machine, i::Index event_id );

#endif

    private:
        OsMutex m_lock;                     ///< Common for all state machines for the manager
        OsMutex m_dispacher_lock;           ///< Fulls protection if user desides to use
                                            ///< dispatch API along with "normal" manager's thread
        OsFastSemaphore m_event_queue_signal;///< Event handling semaphore
        OsThreadHandle m_thread_handle;     ///< Thread Id manager runs in

        Queue m_system_events;              ///< Highest priority system events
        Queue m_interal_events;             ///< Lower priority internal events
        Queue m_external_events;            ///< Lowest priority external events
        bool m_is_parser;                   ///< Do not wait on semaphore for the events 

        Callback m_dispather_host_callback; ///< When dispatch API is used this callback will be invoked
                                            ///< to signal the host system to call Manager::dispatch( )
        void* m_dispather_host_param;       ///< Parameter to the function above
    };
}

#endif
